/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: vfs_msvc.cpp
 *
 * Purpose: virtual file system implementation with windows(visual studio)
 *
 * Developer:
 *   wen.gu , 2019-10-16
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/

#include "config.h"

#if defined(HAS_POSIX_FILE_API) && HAS_POSIX_FILE_API
#include "icpp/core/vfs.h"   
#include <fcntl.h> 
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>

#define LOG_TAG "vfsp"
#include "icpp/core/log.h"

namespace icpp
{
namespace core
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/
#define INVALID_FILE_HANDLE (-1)
/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/




/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/


class VFS::impl
{
public:
    int32_t mFileHdl = INVALID_FILE_HANDLE;


public:
    uint32_t getFileOpenMode(OpenMode mode);
    bool setPosition(int64_t offset);
};

uint32_t VFS::impl::getFileOpenMode(OpenMode mode)
{
    uint32_t retMode = O_RDONLY;
    switch (mode)
    {
    case OpenMode::R:
    {
        retMode = O_RDONLY;
        break;
    }    
    case OpenMode::W:
    {
        retMode = O_WRONLY;
        break;
    }  
    case OpenMode::WCA:
    {
        retMode = O_WRONLY | O_CREAT | O_APPEND;
        break;
    }    
    case OpenMode::WCT:
    {
        retMode = O_WRONLY | O_CREAT | O_TRUNC;
        break;
    } 
    case OpenMode::RW:
    {
        retMode = O_RDWR;
        break;
    } 
    case OpenMode::RWA:
    {
        retMode = O_RDWR | O_APPEND;
        break;
    }
    case OpenMode::RWC:
    {
        retMode = O_RDWR | O_CREAT;
        break;
    }
    case OpenMode::RWT:
    {
        retMode = O_RDWR | O_TRUNC;
        break;
    }
    case OpenMode::RWCA:
    {
        retMode = O_RDWR | O_CREAT | O_APPEND;
        break;
    }    
    case OpenMode::RWCT:
    {
        retMode = O_RDWR | O_CREAT | O_TRUNC;
        break;
    }    
    default:
        break;
    }

    return retMode;
}

bool VFS::impl::setPosition(int64_t offset)
{
    int64_t res = lseek(mFileHdl, offset, SEEK_SET);

    return (res >= 0); /** todo refine me?? */
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
VFS::VFS()
    :impl_(new impl)
{
    /** todo something */
}

VFS::~VFS()
{
    /** todo something */
}
 
/*
    *@brief open a file
    *@param name [in] the name of file(include path).
    *@param mode [in] open file with input mode
    *@return if sucess return OK, else return error code.
    *
    **/
IcppErrc VFS::fileOpen(const std::string& fileName, OpenMode mode)
{
    if (impl_->mFileHdl != INVALID_FILE_HANDLE)
    {
        fileClose();
    }

    uint32_t opMode = impl_->getFileOpenMode(mode);

    impl_->mFileHdl = open(fileName.c_str(), opMode);

    if (impl_->mFileHdl == INVALID_FILE_HANDLE)
    {
        LOGE("open file(%s) failed(%s)\n", fileName.c_str(), strerror(errno));
        return IcppErrc::InsufficientResources;
    }

    return IcppErrc::OK;

}

void VFS::fileClose()
{
    if (impl_->mFileHdl != INVALID_FILE_HANDLE)
    {
        close(impl_->mFileHdl);
        impl_->mFileHdl = INVALID_FILE_HANDLE;
    }
}

/*
*@brief read data from file
*@param offset [in] the offset in file
*@param buf    [io] data buffer
*@param size   [in] data size
*@return if sucess return really read size, else return -1.
*
*@note if read to file end, will be return '0'
*
**/
int32_t VFS::fileRead(int64_t offset, void* buf, uint32_t size)
{
    if (impl_->mFileHdl != INVALID_FILE_HANDLE)
    {
        if (impl_->setPosition(offset))
        {
            int32_t readRet = read(impl_->mFileHdl, buf,size);

            if (readRet >= 0)
            {
                return readRet;
            }
        }

        //todo get error.
    }

    return -1;
}

/*
*@brief write data to file
*@param offset [in] the offset in file
*@param buf    [io] data buffer
*@param size   [in] data size
*@return if sucess return really write size, else return -1.
*
**/
int32_t VFS::fileWrite(int64_t offset, const void* buf, uint32_t size)
{
    if (impl_->mFileHdl != INVALID_FILE_HANDLE)
    {
        if (impl_->setPosition(offset))
        {
            int32_t writeRet = write(impl_->mFileHdl, buf, size);

            if (writeRet >= 0)
            {
                return writeRet;
            }
        }

        //todo get error.
    }

    return -1;
}


/*
*@brief sync file data
*@return if sucess return OK, else return error code.
*
**/
IcppErrc VFS::fileSync()
{
    if (impl_->mFileHdl != INVALID_FILE_HANDLE)
    {
        int32_t ret = fsync(impl_->mFileHdl);

        if (ret >= 0)
        {
            return IcppErrc::OK;
        }
        else
        {
            LOGE("do file sync failed, err(%d,%s)\n",
                errno, strerror(errno));
            return IcppErrc::Undefined;
        }
    }

    return IcppErrc::NoInit;
}

/*
*@brief get file size
*@return if sucess return file size, else return '0'.
*
**/
int64_t VFS::fileSize()
{
    if (impl_->mFileHdl != INVALID_FILE_HANDLE)
    {
        struct stat fileStat = { 0 };

        int32_t ret = fstat(impl_->mFileHdl, &fileStat);

        if (ret == 0)
        {
            return (int64_t)(fileStat.st_size);
        }
        else
        {
            LOGE("get file size with fsate failed,err(%s)\n", strerror(errno));
        }
    }

    return 0;
}

} /** namespace core */
} /** namespace icpp */

#endif /** defined HAVE_POSIX_FILE_API */
